
#include <BFont>
#include <BStyle>
#include "member_BTableWidget.h"
#include "member_BStyle.h"

using namespace BWE;

member_BTableWidget::member_BTableWidget(BTableWidget* tableWidget)
{
	boss = tableWidget;
	gridStyle = Grid_None;
	selectMode = Select_Multiple;
	selecting = false;
	hoverItem = 0;
	shiftItem = 0;
	dirty = false;
	columnHeader.orientation = Orientation_Horizontal;
	rowHeader.orientation = Orientation_Vertical;
	selectedTime = 0;
}
member_BTableWidget::~member_BTableWidget()
{

}

BTableItem* member_BTableWidget::itemAtPoint(const BPoint& pos)
{
	if (items.empty())
		return 0;

	const BRect& crect = boss->centerRect();
	if (!crect.contain(pos))
		return 0;

	int row = -1;
	for (int i = 0; i < rowHeader.sections.size(); i++)
	{
		IHeaderSection& sec = rowHeader.sections[i];
		BRect rect = sec.rect;
		if (rect.top() < pos.y() && rect.bottom() > pos.y())
		{
			row = i;
			break;
		}
	}
	int column = -1;
	for (int i = 0; i < columnHeader.sections.size(); i++)
	{
		IHeaderSection& sec = columnHeader.sections[i];
		BRect rect = sec.rect;
		if (rect.left() < pos.x() && rect.right() > pos.x())
		{
			column = i;
			break;
		}
	}
	if (row >= 0 && column >= 0)
	{
		return items[row][column];
	}
	return 0;
}

void member_BTableWidget::freshColumnHeader(const BStyle* style)
{
	const BFont* font = 0;
	if (headerStyle)
		font = headerStyle->font();
	if (!font)
		font = style->font();

	columnHeader.resizable = false;
	int hintWidth = columnHeader.spacing;
	int hintHeight = 0;
	int realWidth = columnHeader.spacing;
	int realHeight = 0;
	for (int c = 0; c < columnHeader.sections.size(); c++)
	{
		IHeaderSection& section = columnHeader.sections[c];
		if (section.dirtyItems)
		{
			section.itemsSize.reset();
			for (int r = 0; r < items.row(); r++)
			{
				BTableItem* item = items[r][c];
				if (!item)
					continue;
				if (item_member(item)->dirty)
				{
					item_member(item)->freshSize(font);
					item_member(item)->dirty = false;
				}
				int itemWidth = item_member(item)->hintSize.width();
				if (section.itemsSize.width() < itemWidth)
					section.itemsSize.width() = itemWidth;
			}
			section.dirtyItems = false;
		}
		if (section.dirty)
		{
			section.freshSize(headerStyle, font);
			section.rect.setWidth(0);
		}
		if (section.realSize.width() < 1)
			section.realSize.width() = section.hintSize.width();
		if (section.policy == Policy_Fixed)
			section.hintSize.width() = section.realSize.width();
		if (section.policy > Policy_Fixed)
		{
			if (section.policy & Policy_Preferred)
				section.realSize.width() = section.hintSize.width();
			if (section.policy & Policy_Dynamic)
				columnHeader.resizable = true;
		}
		hintWidth += section.hintSize.width() + columnHeader.spacing;
		if (hintHeight < section.hintSize.height())
			hintHeight = section.hintSize.height();
		realWidth += section.realSize.width() + columnHeader.spacing;
		if (realHeight < section.realSize.height())
			realHeight = section.realSize.height();
	}
	columnHeader.hintSize.set(hintWidth, hintHeight);
	columnHeader.realSize.set(realWidth, realHeight);
}
void member_BTableWidget::freshRowHeader(const BStyle* style)
{
	const BFont* font = 0;
	if (headerStyle)
		font = headerStyle->font();
	if (!font)
		font = style->font();

	rowHeader.resizable = false;
	int hintWidth = 0;
	int hintHeight = rowHeader.spacing;
	int realWidth = 0;
	int realHeight = rowHeader.spacing;
	for (int r = 0; r < rowHeader.sections.size(); r++)
	{
		IHeaderSection& section = rowHeader.sections[r];
		if (section.dirtyItems)
		{
			section.itemsSize.reset();
			for (int c = 0; c < items.column(); c++)
			{
				if (BTableItem* item = items[r][c])
				{
					if (item_member(item)->dirty)
					{
						item_member(item)->freshSize(font);
						item_member(item)->dirty = false;
					}
					int itemHeight = item_member(item)->hintSize.height();
					if (section.itemsSize.height() < itemHeight)
						section.itemsSize.height() = itemHeight;
				}
			}
			section.dirtyItems = false;
		}
		if (section.dirty)
		{
			section.freshSize(headerStyle, font);
			section.rect.setWidth(0);
		}
		if (section.realSize.height() < 1)
			section.realSize.height() = section.hintSize.height();
		if (section.policy == Policy_Fixed)
			section.hintSize.height() = section.realSize.height();
		if (section.policy > Policy_Fixed)
		{
			if (section.policy & Policy_Preferred)
				section.realSize.height() = section.hintSize.height();
			if (section.policy & Policy_Dynamic)
				rowHeader.resizable = true;
		}
		if (hintWidth < section.hintSize.width())
			hintWidth = section.hintSize.width();
		hintHeight += section.hintSize.height() + rowHeader.spacing;
		if (realWidth < section.realSize.width())
			realWidth = section.realSize.width();
		realHeight += section.realSize.height() + rowHeader.spacing;
	}
	rowHeader.hintSize.set(hintWidth, hintHeight);
	rowHeader.realSize.set(realWidth, realHeight);
}

void member_BTableWidget::adjustHeader()
{
	const BStyle* style = boss->realStyle();
	freshColumnHeader(style);
	freshRowHeader(style);

	if (columnHeader.visible && boss->perch(Part_Top) != columnHeader.hintSize.height())
	{
		boss->setPerch(Part_Top, columnHeader.hintSize.height());
	}
	if (rowHeader.visible && boss->perch(Part_Left) != rowHeader.hintSize.width())
	{
		boss->setPerch(Part_Left, rowHeader.hintSize.width());
	}

	BRect area = boss->area();
	area.setSize(columnHeader.realSize.width(), rowHeader.realSize.height());
	boss->setArea(area);
	area = boss->area();

	if (columnHeader.resizable && area.width() != columnHeader.hintSize.width())
	{
		columnHeader.distributeWidth(area.width());
		columnHeader.realSize.width() = area.width();
	}
	if (rowHeader.resizable && area.height() != rowHeader.hintSize.height())
	{
		rowHeader.distributeHeight(area.height());
		rowHeader.realSize.height() = area.height();
	}

	columnHeader.show_begin = 0;
	columnHeader.show_end = -1;
	const BRect& topRect = boss->rect(Part_Top);
	int x = area.left();
	for (int i = 0; i < columnHeader.sections.size(); i++)
	{
		x += columnHeader.spacing;
		IHeaderSection& section = columnHeader.sections[i];
		if (section.rect.left() != x || section.rect.width() != section.realSize.width())
		{
			section.rect.left() = x;
			section.rect.right() = x + section.realSize.width();
			section.adjustRect();
		}
		if (section.rect.top() != topRect.top() || section.rect.bottom() != topRect.bottom())
		{
			section.rect.top() = topRect.top();
			section.rect.bottom() = topRect.bottom();
			section.adjustRect();
		}
		if (columnHeader.show_begin < 0 && section.rect.right() > topRect.left())
			columnHeader.show_begin = i;
		if (columnHeader.show_begin >= 0 && section.rect.left() < topRect.right())
			columnHeader.show_end = i;
		x += section.realSize.width();
	}

	rowHeader.show_begin = 0;
	rowHeader.show_end = -1;
	const BRect& leftRect = boss->rect(Part_Left);
	int y = area.top();
	for (int i = 0; i < rowHeader.sections.size(); i++)
	{
		y += rowHeader.spacing;
		IHeaderSection& section = rowHeader.sections[i];
		if (section.rect.top() != y || section.rect.height() != section.realSize.height())
		{
			section.rect.top() = y;
			section.rect.bottom() = y + section.realSize.height();
			section.adjustRect();
		}
		if (section.rect.left() != leftRect.left() || section.rect.right() != leftRect.right())
		{
			section.rect.left() = leftRect.left();
			section.rect.right() = leftRect.right();
			section.adjustRect();
		}
		if (rowHeader.show_begin < 0 && section.rect.bottom() > leftRect.top())
			rowHeader.show_begin = i;
		if (rowHeader.show_begin >= 0 && section.rect.top() < leftRect.bottom())
			rowHeader.show_end = i;
		y += section.rect.height();
	}

}
void member_BTableWidget::adjustItemRects()
{
	const BFont* font = boss->font();
	visualItems.reset();
	for (int r = rowHeader.show_begin; r <= rowHeader.show_end; r++)
	{
		int y = rowHeader.sections[r].rect.y();
		int h = rowHeader.sections[r].rect.height();
		for (int c = columnHeader.show_begin; c <= columnHeader.show_end; c++)
		{
			BTableItem* item = items[r][c];
			if (item == 0)
				continue;
			if (item_member(item)->dirty)
			{
				item_member(item)->freshSize(font);
				item_member(item)->dirty = false;
			}
			int x = columnHeader.sections[c].rect.x();
			int w = columnHeader.sections[c].rect.width();
			if (w > 0 && h > 0)
			{
				item_member(item)->adjustRect(x, y, w, h);
			}
			visualItems.append(item);
		}
	}
	for (int r = 0; r < items.row(); r++)
	{
		int y = rowHeader.sections[r].rect.y();
		int h = rowHeader.sections[r].rect.height();
		for (int c = 0; c < items.column(); c++)
		{
			BTableItem* item = items[r][c];
			if (item == 0)
				continue;
			int x = columnHeader.sections[c].rect.x();
			int w = columnHeader.sections[c].rect.width();
			if (w > 0 && h > 0)
			{
				item_member(item)->adjustRect(x, y, w, h);
			}
		}
	}
}
bool member_BTableWidget::selectItemsInside()
{
	BTableItemArray collectedItems;
	BRange rowRange = rowHeader.yrange(selectRect.top(), selectRect.bottom());
	if (selectMode & Select_Rows)
	{
		for (int r = rowRange.min(); r < rowRange.max(); r++)
		{
			if (BTableItem* item = items(r, 0))
				collectedItems.append(item);
		}
	}
	else
	{
		BRange columnRange = columnHeader.xrange(selectRect.left(), selectRect.right());
		for (int r = rowRange.min(); r < rowRange.max(); r++)
		{
			for (int c = columnRange.min(); c < columnRange.max(); c++)
			{
				if (BTableItem* item = items(r, c))
					collectedItems.append(item);
			}
		}
	}
	bool selectionChanged = false;
	for (int i = 0; i < selectedItems.size(); i++)
	{
		BTableItem* temp = selectedItems[i];
		if (!collectedItems.contain(temp))
		{
			selectedItems.remove(temp);
			item_member(temp)->selected = false;
			emit(Signal_ItemSelected, temp);
			selectionChanged = true;
		}
	}
	for (int i = 0; i < collectedItems.size(); i++)
	{
		BTableItem* temp = collectedItems[i];
		if (!selectedItems.contain(temp))
		{
			selectedItems.append(temp);
			item_member(temp)->selected = true;
			emit(Signal_ItemSelected, temp);
			selectionChanged = true;
		}
	}
	selectedItems = collectedItems;
	return selectionChanged;
}
bool member_BTableWidget::sortColumn(int column)
{
	char mode = columnHeader.sections[column].sortMode;
	if (mode == 0)
		return false;
	for (int row = 0; row < items.row(); row++)
	{
		int select = row;
		BTableItem* setItemSelected = items[row][column];
		if (!setItemSelected)
			continue;
		for (int r = row + 1; r < items.row(); r++)
		{
			BTableItem* item = items[r][column];
			if (!item)
				continue;
			if (mode < 0 && setItemSelected->text() > item->text())
			{
				setItemSelected = item;
				select = r;
			}
			if (mode > 0 && setItemSelected->text() < item->text())
			{
				setItemSelected = item;
				select = r;
			}
		}
		if (select != row)
		{
			items.moveRow(select, row);
		}
		for (int c = 0; c < items.column(); c++)
		{
			BTableItem* item = items[row][c];
			item_member(item)->row = row;
		}
	}
	dirty = true;
	return true;
}
